﻿ #region Disclaimer/Info
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
 //
 //   File:		NHibernateHelper.cs
 //   Website:		http://dexterblogengine.com/
 //   Authors:		http://dexterblogengine.com/About.ashx
 //   Rev:		1
 //   Created:		19/01/2011
 //   Last edit:		19/01/2011
 //   License:		GNU Library General Public License (LGPL)
 //   File:            NHibernateHelper.cs
 //   For updated news and information please visit http://dexterblogengine.com/
 //   Dexter is hosted to Codeplex at http://dexterblogengine.codeplex.com
 //   For any question contact info@dexterblogengine.com
 //
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
 #endregion

using System;
using System.Configuration;
using System.Data;
using System.Text.RegularExpressions;
using Dexter.Domain.Model;
using Dexter.Repository.NHibernate.Configuration;
using Dexter.Repository.NHibernate.ConfORM;
using Dexter.Repository.NHibernate.Drivers;
using Dexter.Repository.NHibernate.Listeners;
using Dexter.Repository.NHibernate.Queries;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Cfg.Loquacious;
using NHibernate.Cfg.MappingSchema;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Tool.hbm2ddl;

namespace Dexter.Repository.NHibernate.Helpers
{
	/// <summary>
	/// The implementation of <see cref="INHibernateHelper"/>.
	/// </summary>
	public class NHibernateHelper : INHibernateHelper
	{
		global::NHibernate.Cfg.Configuration configuration;

		/// <summary>
		/// Gets or sets the session factory.
		/// </summary>
		/// <value>The session factory.</value>
		public ISessionFactory SessionFactory { get; set; }

		readonly INHibernateConfiguration hostConfiguration;

		/// <summary>
		/// Initializes a new instance of the <see cref="NHibernateHelper"/> class.
		/// </summary>
		/// <param name="hostConfiguration">The host environment.</param>
		public NHibernateHelper ( INHibernateConfiguration hostConfiguration ) {
			this.hostConfiguration = hostConfiguration;
		}

		/// <summary>
		/// Return the NHibernate configuration.
		/// </summary>
		/// <value>The configuration.</value>
		public global::NHibernate.Cfg.Configuration Configuration {
			get { return configuration; }
		}

		/// <summary>
		/// Configures NHibernate
		/// </summary>
		public void Configure ( ) {
			if(!hostConfiguration.IsConfigured)
				return;

			string databaseObjects = null;
			configuration = new global::NHibernate.Cfg.Configuration();
			configuration.SessionFactoryName("Dexter");
			configuration.DataBaseIntegration(db =>
			{
				GetDataBaseDialect ( hostConfiguration, db, hostConfiguration.DatabaseSchema, out databaseObjects );
				db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
				db.IsolationLevel = IsolationLevel.ReadCommitted;
				db.ConnectionString = hostConfiguration.ConnectionString;
				db.Timeout = 10;
				db.HqlToSqlSubstitutions = "true 1, false 0, yes 'Y', no 'N'";
				db.PrepareCommands = true;
			});

			configuration.Properties[global::NHibernate.Cfg.Environment.DefaultSchema] = hostConfiguration.DatabaseSchema;
			configuration.Properties[global::NHibernate.Cfg.Environment.GenerateStatistics] = "true";
			configuration.Properties[global::NHibernate.Cfg.Environment.PrepareSql] = "true";
			configuration.Properties[global::NHibernate.Cfg.Environment.Hbm2ddlKeyWords] = "auto-quote";
			configuration.Imports.Add(typeof(Month).Name, typeof(Month).AssemblyQualifiedName);

			configuration.EventListeners.PreInsertEventListeners = new[] {
				new EntityBaseSaveOrUpdateListener ( )
			};
			configuration.EventListeners.PreUpdateEventListeners = new[] {
				new EntityBaseSaveOrUpdateListener ( )
			};

			if (!string.IsNullOrEmpty(databaseObjects))
				configuration.AddXmlString ( databaseObjects);

			configuration.AddDeserializedMapping(this.GetMapping(), "Dexter_Domain");

			this.RegisterRepositoryQueries();

			SessionFactory = configuration.BuildSessionFactory();
		}

		/// <summary>
		/// Gets the session.
		/// </summary>
		/// <returns></returns>
		public ISession GetSession ( ) {
			return SessionFactory.OpenSession ( );
		}

		/// <summary>
		/// Checks if the database schema should be updated.
		/// </summary>
		/// <returns>
		/// 	<c>True</c> if the schema requires the update;  otherwise, <c>false</c>.
		/// </returns>
		public bool ShouldUpdateSchema ( ) {
			var validator = new SchemaValidator(configuration);
			validator.Validate();

			return false;
		}

		/// <summary>
		/// Updates the database schema.
		/// </summary>
		public void UpdateSchema ( ) {
			var update = new SchemaUpdate(configuration);
			update.Execute(false, true);
		}

		/// <summary>
		/// Initializes the schema.
		/// </summary>
		public void InitializeSchema ( ) {
			var export = new SchemaExport(configuration);
			export.Execute(false, true, false);
		}

		/// <summary>
		/// Drops the schema.
		/// </summary>
		public void DropSchema()
		{
			var export = new SchemaExport(configuration);
			export.Execute(false, true, true);
		}

		/// <summary>
		/// Gets the get connection string.
		/// </summary>
		/// <value>The get connection string.</value>
		public string GetConnectionString {
			get { return configuration.Properties[ "connection.connection_string" ]; }
		}

		static void GetDataBaseDialect(INHibernateConfiguration nHibernateConfiguration, IDbIntegrationConfigurationProperties db,string dbSchema, out string databaseObjects)
		{
			var databaseType = ( DbType )Enum.Parse ( typeof ( DbType ), nHibernateConfiguration.DatabaseType);
			switch (databaseType)
			{
				case DbType.MsSql2000:
					db.Dialect<MsSql2000Dialect>();
					db.Driver<DexterSqlClientDriver> ( );
					databaseObjects = Resources.DatabaseObjects.MsSQL2000;

					if (databaseObjects != null)
						databaseObjects = Regex.Replace ( databaseObjects , @"(\[.*\])" , string.Format ( "[{0}].$1" , dbSchema ) );

					break;
				case DbType.MsSql2005:
					db.Dialect<MsSql2005Dialect>();
					db.Driver<DexterSqlClientDriver> ( );
					databaseObjects = Resources.DatabaseObjects.MsSQL2005;

					if (databaseObjects != null)
						databaseObjects = Regex.Replace(databaseObjects, @"(\[.*\])", string.Format("[{0}].$1", dbSchema));

					break;
				case DbType.MsSql2008:
					db.Dialect<MsSql2008Dialect>();
					db.Driver<DexterSqlClientDriver> ( );
					databaseObjects = Resources.DatabaseObjects.MsSQL2008;

					if (databaseObjects != null)
						databaseObjects = Regex.Replace(databaseObjects, @"(\[.*\])", string.Format("[{0}].$1", dbSchema));

					break;
				case DbType.Azure:
					db.Dialect<MsSqlAzure2008Dialect>();
					db.Driver<SqlClientDriver> ( );
					databaseObjects = Resources.DatabaseObjects.MsSQL2008;

					if (databaseObjects != null)
						databaseObjects = Regex.Replace ( databaseObjects, @"(\[.*\])", string.Format ( "[{0}].$1", dbSchema ) );

					break;
				case DbType.MySql:
					db.Dialect<MySQLDialect>();
					db.Driver<MySqlDataDriver>();
					databaseObjects = null;
					break;
				case DbType.MySql5:
					db.Dialect<MySQL5Dialect>();
					db.Driver<MySqlDataDriver>();
					databaseObjects = null;
					break;
				case DbType.Oracle9:
					db.Dialect<Oracle9iDialect>();
					db.Driver<OracleClientDriver>();
					databaseObjects = null;
					break;
				case DbType.Oracle10:
					db.Dialect<Oracle10gDialect>();
					db.Driver<OracleClientDriver>();
					databaseObjects = null;
					break;
				case DbType.SqlLite:
					db.Dialect<SQLiteDialect>();
					db.Driver<SQLite20Driver>();
					databaseObjects = null;
					break;
				case DbType.SqlLiteInMemory:
					db.Dialect<SQLiteDialect>();
					db.Driver<SQLite20Driver>();
					databaseObjects = null;
					break;
				default:
					throw new ConfigurationErrorsException("The specified database is not supported");
			}
		}

		HbmMapping GetMapping()
		{
			var mapper = new DomainMapper ( configuration, hostConfiguration );
			return mapper.Mapper.CompileMappingFor(mapper.DomainEntities);
		}

	}
}
